package main

import (
	l4g "base/log4go"

	"base/common"
	"base/radix/redis"
	"time"
)

var (
	g_redis    *redis.Client
	gMultiCall *redis.MultiCall

	g_redis_write_queue = make([]RedisObject, 0, 32)
)

type RedisKeyType uint32

const (
	RedisHashKey = 1
	RedisMapKey  = 2
	RedisSetKey  = 3
)

type RedisObject interface {
	KeyName() string
	KeyType() RedisKeyType
	Save() []interface{}
	Load([][]byte) error
	Fields() []interface{}
	UnModify()
}

type UserInfo struct {
	modified bool
	Lvl      uint32
	Age      uint32
}

func (this *UserInfo) KeyName() string {
	return "user"
}

func (this *UserInfo) KeyType() RedisKeyType {
	return RedisHashKey
}

func (this *UserInfo) Save() []interface{} {
	ret := make([]interface{}, 0, 32)
	ret = append(ret, "lvl", this.Lvl)
	ret = append(ret, "age", this.Age)
	return ret
}

func (this *UserInfo) Load(bytes [][]byte) error {
	lvl, _ := common.ParseUint64(bytes[0])
	age, _ := common.ParseUint64(bytes[1])
	this.Lvl = uint32(lvl)
	this.Age = uint32(age)
	return nil
}

func (this *UserInfo) Fields() []interface{} {
	return []interface{}{"lvl", "age"}
}

func (this *UserInfo) MarkModify() {
	if this.modified == false {
		g_redis_write_queue = append(g_redis_write_queue, this)
		this.modified = true
	}
}

func (this *UserInfo) UnModify() {
	this.modified = false
}

func FlushRedis() {
	for _, obj := range g_redis_write_queue {
		redis_save_req_chan <- RedisSaveReq{
			KeyName: obj.KeyName(),
			KeyType: obj.KeyType(),
			Params:  obj.Save(),
		}
		obj.UnModify()
	}
	g_redis_write_queue = g_redis_write_queue[0:0]
}

type RedisLoadReq struct {
	KeyName  string
	KeyType  RedisKeyType
	Fields   []interface{}
	Callback func([][]byte, error)
}

type RedisLoadRes struct {
	Bytes    [][]byte
	Err      error
	Callback func([][]byte, error)
}

type RedisSaveReq struct {
	KeyType RedisKeyType
	KeyName string
	Params  []interface{}
}

func LoadRedisKey(obj RedisObject, f func(error)) {
	redis_load_req_chan <- RedisLoadReq{
		KeyName: obj.KeyName(),
		KeyType: obj.KeyType(),
		Fields:  obj.Fields(),
		Callback: func(bytes [][]byte, err error) {
			if err == nil {
				err = obj.Load(bytes)
			}
			f(err)
		},
	}
}

var (
	redis_load_req_chan = make(chan RedisLoadReq, 1024)
	redis_load_res_chan = make(chan RedisLoadRes, 1024)
	redis_save_req_chan = make(chan RedisSaveReq, 1024)
	net_req             = make(chan int, 1024)
)

func redis_work() {
	l4g.LoadConfiguration("orm_log.xml")
	defer l4g.Close()

	rdConf := redis.DefaultConfig()
	rdConf.Network = "tcp"
	rdConf.Address = "127.0.0.7:6382"
	rdConf.Database = 13
	if rd, err := redis.NewClient(rdConf); err != nil {
		l4g.Error("conn redis fail: %v %v", rdConf, err)
		return
	} else {
		g_redis = rd
	}
	defer g_redis.Close()
	gMultiCall = redis.NewMultiCall(g_redis)

	for {
		select {
		case req := <-redis_load_req_chan:
			l4g.Debug("Redis Req %+v", req)
			bytes, err := g_redis.Hmget(req.KeyName, req.Fields).ListBytes()
			redis_load_res_chan <- RedisLoadRes{
				Bytes:    bytes,
				Err:      err,
				Callback: req.Callback,
			}
		case req := <-redis_save_req_chan:
			g_redis.Hmset(req.KeyName, req.Params)
		}
	}
}

func logic_work() {
	user := &UserInfo{}
	for {
		select {
		case <-net_req:
			LoadRedisKey(user, func(err error) {
				l4g.Debug("Fk33 Return %+v %+v", user, err)
				user.Lvl = 6
				user.Age = 9
				user.MarkModify()
				l4g.Debug("Modified")
			})
		case res := <-redis_load_res_chan:
			l4g.Debug("Redis Res %+v", res)
			res.Callback(res.Bytes, res.Err)
		}
		FlushRedis()
	}
}

func main() {
	go redis_work()
	go logic_work()
	net_req <- 1
	time.Sleep(time.Hour)
}
